#ifndef Mutex_INCLUDED
#define Mutex_INCLUDED

#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/time.h>

class Mutex
{
public:
    Mutex(bool fast = true);
        
    ~Mutex();

    void lock();
        
    bool lock(long milliseconds);

    bool tryLock();

    bool tryLock(long milliseconds);

    void unlock();

private:
    Mutex(const Mutex&);
    Mutex& operator = (const Mutex&);

private:
    pthread_mutex_t     m_mutex;
};

class ScopedMutex
{
public:
    explicit ScopedMutex(Mutex* mutex)
    {
        m_mutex = NULL;
        if ( mutex != NULL )
        {
            m_mutex = mutex;
            m_mutex->lock();
        }
    }

    ~ScopedMutex()
    {
        if ( m_mutex != NULL )
            m_mutex->unlock();
    }

private:
    ScopedMutex& operator=(const ScopedMutex& other);
    ScopedMutex(const ScopedMutex& other);

private:
    Mutex* m_mutex;
};

//
// inlines
//
inline void Mutex::lock()
{
    pthread_mutex_lock(&m_mutex);
}

inline bool Mutex::lock(long milliseconds)
{
    struct timespec abstime;
    struct timeval tv;
    gettimeofday(&tv, NULL);
    int nsec = milliseconds / 1000;
    abstime.tv_sec  = tv.tv_sec + nsec;
    abstime.tv_nsec = tv.tv_usec + (milliseconds - nsec*1000)*1000000;
    if (abstime.tv_nsec >= 1000000000)
    {
        abstime.tv_nsec -= 1000000000;
        abstime.tv_sec++;
    }
    int rc = pthread_mutex_timedlock(&m_mutex, &abstime);
    if (rc == 0)
        return true;
    else if (rc == ETIMEDOUT)
        return false;

    return false;
}

inline void Mutex::unlock()
{
    pthread_mutex_unlock(&m_mutex);
}

inline bool Mutex::tryLock(long milliseconds)
{
    return lock(milliseconds);
}

inline bool Mutex::tryLock()
{
    int rc = pthread_mutex_trylock(&m_mutex);
    if (rc == 0)
        return true;
    else if (rc == EBUSY)
        return false;

    return false;
}

#endif



